#pragma once
#include "base/core/com_define.h"
#include "math.h"

template<typename TYPE = int>
class Point3 
{
public:
	TYPE  x, y, z;

public:
	inline Point3() 
	{
		x = 0;
		y = 0;
		z = 0;
	}

	inline Point3(const TYPE& ValueX, const TYPE& ValueY, const TYPE& ValueZ)
		:x(ValueX), y(ValueY), z(ValueZ) {}

	inline explicit Point3(const TYPE Value)
		:x(Value), y(Value), z(Value) {}

	inline explicit Point3(const TYPE Vec[3])
		:x(Vec[0]), y(Vec[1]), z(Vec[2]) {}

	inline explicit Point3(TYPE* const r)
		:x(r[0]), y(r[1]), x(r[2]) {}

	inline TYPE operator[] (const size_t i) const 
	{
		MMO_ASSERT(i < 3);
		return *(&x+i);
	}

	inline TYPE& operator[] (const size_t i) 
	{
		MMO_ASSERT(i < 3);
		return *(&x+i);
	}

	inline Point3& operator = (const Point3& Value) 
	{
		x = Value.x;
		y = Value.y;
		z = Value.z;
		return (*this);
	}

	inline Point3& operator = (const TYPE Value) 
	{
		x = Value;
		y = Value;
		z = Value;
		return (*this);
	}

	inline bool operator == (const Point3& Value) const 
	{
		return (x == Value.x && y == Value.y && z == Value.z);
	}

	inline bool operator != (const Point3& Value) const 
	{
		return (x != Value.x || y != Value.y ||  z != Value.z);
	}

	inline Point3 operator + (const Point3& Value) const 
	{
		return Point3((x + Value.x), (y + Value.y), (z + Value.z));
	}

	inline Point3 operator - (const Point3& Value) const 
	{
		return Point3((x - Value.x), (y - Value.y), (z - Value.z));
	}

	inline Point3 operator * (const TYPE Value) const 
	{
		return Point3((x * Value), (y * Value), (z * Value));
	}

	inline Point3 operator * (const Point3& Value) const 
	{
		return Point3((x * Value.x), (y * Value.y), (z * Value));
	}

	inline Point3 operator / (const TYPE Value) const 
	{
		MMO_ASSERT(Value != 0);
		return Point3((x / Value), (y / Value), (z / Value));
	}

	inline Point3 operator / (const Point3& Value) const 
	{
		MMO_ASSERT(Value.x != 0 && Value.y != 0 && Value.z != 0);
		return Point3((x / Value.x), (y / Value.y), (z / Value.z));
	}

	inline const Point3& operator + () const 
	{
		return (*this);
	}

	inline Point3 operator - () const 
	{
		return Point3(-x, -y, -z);
	}

	inline friend Point3 operator * (const TYPE Value, const Point3& point) 
	{
		return Point3((Value * point.x), (Value * point.y), (Value * point.z));
	}

	inline friend Point3 operator / (const TYPE Value, const Point3& point) 
	{
		MMO_ASSERT(point.x != 0 && point.y != 0 && point.z != 0);
		return Point3((Value / point.x), (Value / point.y), (Value / point.z));
	}

	inline friend Point3 operator + (const Point3& point, const TYPE Value) 
	{
		return Point3((point.x + Value), (point.y + Value), (point.z + Value));
	}

	inline friend Point3 operator + (const TYPE Value, const Point3& point) 
	{
		return Point3((Value + point.x), (Value + point.y), (Value + point.z));
	}

	inline friend Point3 operator - (const Point3& point, const TYPE Value) 
	{
		return Point3((point.x - Value), (point.y - Value), point.z - Value);
	}

	inline friend Point3 operator - (const TYPE Value, const Point3& point) 
	{
		return Point3((Value - point.x), (Value - point.y), (Value - point.z));
	}

	inline friend Point3 AbsSub(const Point3& Value1, const Point3& Value2) 
	{
		return Point3(abs(Value2.x - Value1.x), abs(Value2.y - Value1.y), abs(Value2.z - Value1.z));
	}

	inline friend Point3 interpolate(const float alpha, const Point3& Value1, const Point3& Value2)
	{
		return Value1 + ((Value2 - Value1) * alpha);
	}

	inline float length() const 
	{
		return sqrt(x*x + y*y + z*z);
	}

	inline Point3& operator += (const Point3& Value) 
	{
		x += Value.x;
		y += Value.y;
		z += Value.z;
		return (*this);
	}

	inline Point3& operator += (const TYPE Value) 
	{
		x += Value;
		y += Value;
		z += Value;
		return (*this);
	}

	inline Point3& operator -= (const Point3& Value) 
	{
		x -= Value.x;
		y -= Value.y;
		z -= Value.z;
		return (*this);
	}

	inline Point3& operator -= (const TYPE Value) 
	{
		x -= Value;
		y -= Value;
		z -= Value;
		return (*this);
	}

	inline Point3& operator *= (const TYPE Value) 
	{
		x *= Value;
		y *= Value;
		z *= Value;
		return (*this);
	}

	inline Point3& operator *= (const Point3& Value) 
	{
		x *= Value.x;
		y *= Value.y;
		z *= Value.z;
		return (*this);
	}

	inline Point3& operator /= (const TYPE Value) 
	{
		MMO_ASSERT(Value != 0.0f);
		TYPE tempValue = 1.0f / Value;
		x *= tempValue;
		y *= tempValue;
		z *= tempValue;
		return (*this);
	}

	inline Point3& operator /= (const Point3& Value) 
	{
		MMO_ASSERT((Value.x != 0) && (Value.y != 0));
		x /= Value.x;
		y /= Value.y;
		z /= Value.z;
		return (*this);
	}

	inline operator TYPE* () 
	{
		return &x;
	}

	inline operator const TYPE* () const 
	{
		return &x;
	}
};
